Download
pink~.zip —
Pd object (os x), help patch, source code, and matlab parameter calculator
Algorithm
The pinking filter creates pink noise by filtering white noise to create the desired pink energy spectrum. The filter's frequency response is -3dB per octave starting at approximately four Hertz and continuing to just below the Nyquist frequency. The pinking filter consists of several one-pole low-pass filters, where each low-pass is spaced two octaves from its neighbors and filter gains are attenuated in 6dB steps. This configuration results in a nearly linear -3dB per octave overall frequency response when the low-pass filters are summed.
This algorithm is designed to work with any sample rate up to 192kHz. Filter coefficients are calculated by first determining the maximum cutoff frequency resulting in a filter coefficient less than one, described by the following formula where f is frequency and sr is sample rate. The - 1 ensures that the coefficient resulting from f is just below 1 to prevent the first filter from exploding.
f = sr / 2π - 1
while( f > 1 ) { a[i] = 2π * f / sr f = f / 4 i++ } |
while( i-- > 0 ) { g[i] = pow( 10, dB / 20 ) s += g[i]; dB -= 6.0; } og = pow( 10, ( ( -20 * log10( s ) ) / 20 ) |
while( ++i < f ) { y[i] = a[i] * w + ( 1 - a[i] ) * y[i]; p += y[i] * g[i]; } o = og * p; |
Cutoff Frequencies, Coefficients, and Gains
Below are lists of filter pararameters for common audio sample rates. Parameters for other sample rates may be calculated with the pink.m matlab script in
pink~.zip.
Sample Rate : 8000 Output Gain : 0.506845730 Freq Coeff Gain ------------------------------------------- 1.242421430 0.000975796 1.000000000 4.969685722 0.003903182 0.501187234 19.878742886 0.015612728 0.251188643 79.514971546 0.062450913 0.125892541 318.059886184 0.249803650 0.063095734 1272.239544735 0.999214602 0.031622777 |
Sample Rate : 11025 Output Gain : 0.506845730 Freq Coeff Gain ------------------------------------------- 1.712581296 0.000976006 1.000000000 6.850325186 0.003904024 0.501187234 27.401300744 0.015616095 0.251188643 109.605202974 0.062464381 0.125892541 438.420811897 0.249857524 0.063095734 1753.683247588 0.999430097 0.031622777 |
|
Sample Rate : 16000 Output Gain : 0.506845730 Freq Coeff Gain ------------------------------------------- 2.485819423 0.000976179 1.000000000 9.943277693 0.003904716 0.501187234 39.773110773 0.015618864 0.251188643 159.092443092 0.062475456 0.125892541 636.369772368 0.249901825 0.063095734 2545.479089470 0.999607301 0.031622777 |
Sample Rate : 22050 Output Gain : 0.506845730 Freq Coeff Gain ------------------------------------------- 3.426139155 0.000976284 1.000000000 13.704556622 0.003905137 0.501187234 54.818226487 0.015620548 0.251188643 219.272905949 0.062482191 0.125892541 877.091623794 0.249928762 0.063095734 3508.366495176 0.999715048 0.031622777 |
|
Sample Rate : 32000 Output Gain : 0.502806702 Freq Coeff Gain ------------------------------------------- 1.243153852 0.000244093 1.000000000 4.972615409 0.000976371 0.501187234 19.890461636 0.003905483 0.251188643 79.561846546 0.015621932 0.125892541 318.247386184 0.062487728 0.063095734 1272.989544735 0.249950913 0.031622777 5091.958178941 0.999803650 0.015848932 |
Sample Rate : 44100 Output Gain : 0.502806702 Freq Coeff Gain ------------------------------------------- 1.713313718 0.000244106 1.000000000 6.853254873 0.000976423 0.501187234 27.413019494 0.003905693 0.251188643 109.652077974 0.015622774 0.125892541 438.608311897 0.062491095 0.063095734 1754.433247588 0.249964381 0.031622777 7017.732990353 0.999857524 0.015848932 |
|
Sample Rate : 48000 Output Gain : 0.502806702 Freq Coeff Gain ------------------------------------------- 1.864852849 0.000244109 1.000000000 7.459411395 0.000976435 0.501187234 29.837645580 0.003905739 0.251188643 119.350582319 0.015622955 0.125892541 477.402329276 0.062491819 0.063095734 1909.609317103 0.249967275 0.031622777 7638.437268411 0.999869100 0.015848932 |
Sample Rate : 64000 Output Gain : 0.502806702 Freq Coeff Gain ------------------------------------------- 2.486551845 0.000244117 1.000000000 9.946207381 0.000976467 0.501187234 39.784829523 0.003905867 0.251188643 159.139318092 0.015623466 0.125892541 636.557272368 0.062493864 0.063095734 2546.229089470 0.249975456 0.031622777 10184.916357881 0.999901825 0.015848932 |
|
Sample Rate : 88200 Output Gain : 0.502806702 Freq Coeff Gain ------------------------------------------- 3.426871577 0.000244123 1.000000000 13.707486309 0.000976493 0.501187234 54.829945237 0.003905972 0.251188643 219.319780949 0.015623887 0.125892541 877.279123794 0.062495548 0.063095734 3509.116495176 0.249982191 0.031622777 14036.465980705 0.999928762 0.015848932 |
Sample Rate : 96000 Output Gain : 0.502806702 Freq Coeff Gain ------------------------------------------- 3.729949838 0.000244125 1.000000000 14.919799352 0.000976499 0.501187234 59.679197409 0.003905994 0.251188643 238.716789638 0.015623977 0.125892541 954.867158551 0.062495909 0.063095734 3819.468634205 0.249983638 0.031622777 15277.874536822 0.999934550 0.015848932 |
|
Sample Rate : 176400 Output Gain : 0.500806513 Freq Coeff Gain ------------------------------------------- 1.713496824 0.000061033 1.000000000 6.853987295 0.000244132 0.501187234 27.415949181 0.000976528 0.251188643 109.663796724 0.003906111 0.125892541 438.655186897 0.015624443 0.063095734 1754.620747588 0.062497774 0.031622777 7018.482990353 0.249991095 0.015848932 28073.931961410 0.999964381 0.007943282 |
Sample Rate : 192000 Output Gain : 0.500806513 Freq Coeff Gain ------------------------------------------- 1.865035954 0.000061033 1.000000000 7.460143817 0.000244133 0.501187234 29.840575267 0.000976531 0.251188643 119.362301069 0.003906122 0.125892541 477.449204276 0.015624489 0.063095734 1909.796817103 0.062497955 0.031622777 7639.187268411 0.249991819 0.015848932 30556.749073644 0.999967275 0.007943282 |
References
The technique described above was inspired by Paul Kellet's filter coefficients, located here:
· Filter to make pink noise from white
Larry Trammell's method is described here:
· Pink Noise Generator
· Trammell Pink Noise (C++ class)
An excellent reference page about pink noise techniques is located here:
· DSP generation of Pink (1/f) Noise
My exploratory Pd object source code for the Kellet and Trammel techniques is available here:
· pink~.ref.zip
pink~.c
//------------------------------------------------------------------------------
// Pink Noise Object for Pd
//
// pink~.c
//
// Generates pink noise using a series of summed and attenuated lowpass filters
//
// Created by Cooper Baker on 2/4/15.
// Copyright (c) 2015 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------
// main header for pd
#include "m_pd.h"
// standard library for random number generator
#include <stdlib.h>
// time header to seed random numbers
#include <time.h>
// utility header for Pd Spectral Toolkit project
#include "utility.h"
// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
//------------------------------------------------------------------------------
// pink_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* pink_class;
//------------------------------------------------------------------------------
// pink - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct pink
{
// this object - must always be first variable in struct
t_object object;
// filter coefficients
double a[ 8 ];
// filter gains
double g[ 8 ];
// filter states
double y[ 8 ];
// filter gain
double gain;
// number of filters
t_int filters;
// random normalization coefficient
double rand_norm;
} t_pink;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static t_int* pink_perform ( t_int* io );
static void pink_dsp ( t_pink* object, t_signal **sig );
static void* pink_new ( void );
void pink_tilde_setup ( void );
//------------------------------------------------------------------------------
// pink_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* pink_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* out = ( t_float* )( io[ 1 ] );
t_int frames = ( t_int )( io[ 2 ] );
t_pink* object = ( t_pink* )( io[ 3 ] );
// copy pointers to filter value arrays
double* a = object->a;
double* g = object->g;
double* y = object->y;
// copy filter parameters
double gain = object->gain;
t_int filters = object->filters;
// random number variable
double rand_norm = object->rand_norm;
// signal vector iterator variable
t_int n = -1;
// filter iterator variable
t_int i;
// noise variables
double white;
double pink;
// the dsp loop
while( ++n < frames )
{
// make white noise normalized between -1 and 1
white = rand() * rand_norm - 1.0;
// initialize
pink = 0.0;
i = -1;
// the filter loop
while( ++i < filters )
{
// filter the white noise
y[ i ] = a[ i ] * white + ( 1.0 - a[ i ] ) * y[ i ];
// apply gain and accumulate filtered noise
pink += y[ i ] * g[ i ];
}
// apply overall gain and copy to output
out[ n ] = pink * gain;
}
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 4 ] );
}
//------------------------------------------------------------------------------
// pink_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void pink_dsp( t_pink* object, t_signal **sig )
{
// get sample rate
t_float sr = sig[ 0 ]->s_sr;
// copy pointers to filter value arrays
double* a = object->a;
double* g = object->g;
// allocate temporary variables
t_int i = 0;
t_int db = 0;
double amp_sum = 0;
// calculate maximum cutoff frequency so that filter coefficient < 1.0;
double freq = ( sr / C_2_PI ) - 1.0;
// calculate coefficients
while( freq > 1 )
{
a[ i ] = C_2_PI * freq / sr;
freq = freq / 4.0;
i++;
}
// store number of filters
object->filters = i;
// calculate gains
while( i-- > 0 )
{
g[ i ] = DbToA( db );
amp_sum += DbToA( db );
db -= 6.0;
}
// calculate overall gain
object->gain = DbToA( -AToDb( amp_sum ) );
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// outlet sample vector
// sample frames to process (vector size)
// object pointer
dsp_add( pink_perform, 3, sig[ 0 ]->s_vec, sig[ 0 ]->s_n, object );
}
//------------------------------------------------------------------------------
// pink_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* pink_new( void )
{
// create a pointer to this object
t_pink* object = ( t_pink* )pd_new( pink_class );
// create a new signal outlet for this object
outlet_new( &object->object, gensym( "signal" ) );
// calculate 2 over rand max
object->rand_norm = ( 2.0 / RAND_MAX );
// initialize filter values
memset( object->a, 0, 8 * sizeof( double ) );
memset( object->g, 0, 8 * sizeof( double ) );
memset( object->y, 0, 8 * sizeof( double ) );
// seed the random number generator
time_t t;
srand( ( unsigned )time( &t ) );
return object;
}
//------------------------------------------------------------------------------
// pink_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void pink_tilde_setup( void )
{
// creates an instance of this object and describes it to pd
pink_class = class_new( gensym( "pink~" ), ( t_newmethod )pink_new, 0, sizeof( t_pink ), CLASS_NOINLET, 0 );
// installs pink_dsp so that it will be called when dsp is turned on
class_addmethod( pink_class, ( t_method )pink_dsp, gensym( "dsp" ), 0 );
// announce this object in the pd console
Announce( "pink~: pink noise generator - v1.0" );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------
// Pink Noise Object for Pd
//
// pink~.c
//
// Generates pink noise using a series of summed and attenuated lowpass filters
//
// Created by Cooper Baker on 2/4/15.
// Copyright (c) 2015 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------
// main header for pd
#include "m_pd.h"
// standard library for random number generator
#include <stdlib.h>
// time header to seed random numbers
#include <time.h>
// utility header for Pd Spectral Toolkit project
#include "utility.h"
// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
//------------------------------------------------------------------------------
// pink_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* pink_class;
//------------------------------------------------------------------------------
// pink - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct pink
{
// this object - must always be first variable in struct
t_object object;
// filter coefficients
double a[ 8 ];
// filter gains
double g[ 8 ];
// filter states
double y[ 8 ];
// filter gain
double gain;
// number of filters
t_int filters;
// random normalization coefficient
double rand_norm;
} t_pink;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static t_int* pink_perform ( t_int* io );
static void pink_dsp ( t_pink* object, t_signal **sig );
static void* pink_new ( void );
void pink_tilde_setup ( void );
//------------------------------------------------------------------------------
// pink_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* pink_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* out = ( t_float* )( io[ 1 ] );
t_int frames = ( t_int )( io[ 2 ] );
t_pink* object = ( t_pink* )( io[ 3 ] );
// copy pointers to filter value arrays
double* a = object->a;
double* g = object->g;
double* y = object->y;
// copy filter parameters
double gain = object->gain;
t_int filters = object->filters;
// random number variable
double rand_norm = object->rand_norm;
// signal vector iterator variable
t_int n = -1;
// filter iterator variable
t_int i;
// noise variables
double white;
double pink;
// the dsp loop
while( ++n < frames )
{
// make white noise normalized between -1 and 1
white = rand() * rand_norm - 1.0;
// initialize
pink = 0.0;
i = -1;
// the filter loop
while( ++i < filters )
{
// filter the white noise
y[ i ] = a[ i ] * white + ( 1.0 - a[ i ] ) * y[ i ];
// apply gain and accumulate filtered noise
pink += y[ i ] * g[ i ];
}
// apply overall gain and copy to output
out[ n ] = pink * gain;
}
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 4 ] );
}
//------------------------------------------------------------------------------
// pink_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void pink_dsp( t_pink* object, t_signal **sig )
{
// get sample rate
t_float sr = sig[ 0 ]->s_sr;
// copy pointers to filter value arrays
double* a = object->a;
double* g = object->g;
// allocate temporary variables
t_int i = 0;
t_int db = 0;
double amp_sum = 0;
// calculate maximum cutoff frequency so that filter coefficient < 1.0;
double freq = ( sr / C_2_PI ) - 1.0;
// calculate coefficients
while( freq > 1 )
{
a[ i ] = C_2_PI * freq / sr;
freq = freq / 4.0;
i++;
}
// store number of filters
object->filters = i;
// calculate gains
while( i-- > 0 )
{
g[ i ] = DbToA( db );
amp_sum += DbToA( db );
db -= 6.0;
}
// calculate overall gain
object->gain = DbToA( -AToDb( amp_sum ) );
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// outlet sample vector
// sample frames to process (vector size)
// object pointer
dsp_add( pink_perform, 3, sig[ 0 ]->s_vec, sig[ 0 ]->s_n, object );
}
//------------------------------------------------------------------------------
// pink_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* pink_new( void )
{
// create a pointer to this object
t_pink* object = ( t_pink* )pd_new( pink_class );
// create a new signal outlet for this object
outlet_new( &object->object, gensym( "signal" ) );
// calculate 2 over rand max
object->rand_norm = ( 2.0 / RAND_MAX );
// initialize filter values
memset( object->a, 0, 8 * sizeof( double ) );
memset( object->g, 0, 8 * sizeof( double ) );
memset( object->y, 0, 8 * sizeof( double ) );
// seed the random number generator
time_t t;
srand( ( unsigned )time( &t ) );
return object;
}
//------------------------------------------------------------------------------
// pink_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void pink_tilde_setup( void )
{
// creates an instance of this object and describes it to pd
pink_class = class_new( gensym( "pink~" ), ( t_newmethod )pink_new, 0, sizeof( t_pink ), CLASS_NOINLET, 0 );
// installs pink_dsp so that it will be called when dsp is turned on
class_addmethod( pink_class, ( t_method )pink_dsp, gensym( "dsp" ), 0 );
// announce this object in the pd console
Announce( "pink~: pink noise generator - v1.0" );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------